package com.liuhm.controller;/**
 * @author ：liuhm
 * @date ：Created in 2019/4/24 16:08
 */

import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

/**
 * @ClassName：HellowWorld
 * @Description: TODO
 * @Author: liuhm
 * @Date: 2019/4/24 16:08
 */
@RestController
public class TestController {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    /**
     * @description: 这种情况不会出现超卖，但是会出现前面先通过线程来购买的人没有办法购买
     * @params:
     * @return:
     * @author: liuhm
     * @Date: 2020/6/1 0001 下午 10:14
     */
    @GetMapping("/test1")
    public void test1(String name){
        try {
            String lockKey="lock";
            Long num=  stringRedisTemplate.opsForValue().increment("lock",1);
            // 防止获取锁后，减库存的业务突然挂掉，然后重启后锁得不到释放
            stringRedisTemplate.expire(lockKey,10,TimeUnit.SECONDS);
            if(num==1){
                int stock=Integer.parseInt(String.valueOf(stringRedisTemplate.opsForValue().get("stock")));
                if(stock>0){
                    stringRedisTemplate.opsForValue().set("stock",(stock-1)+"");
                    System.out.println(name+"---抢单成功，扣库存成功，库存stock"+(stock-1)+"");
                }else{
                    System.out.println("扣减失败，库存不足");
                }
            }
        }finally {
            stringRedisTemplate.opsForValue().increment("lock",-1);
        }
    }

    /**
     * @description: 会出现排队的情况，一个一个的扣除
     * @params:
     * @return:
     * @author: liuhm
     * @Date: 2020/6/1 0001 下午 10:21
     */
    @GetMapping("/test2")
    public void test2(String name){
        try {
            String lockKey="lock";
            Long num=  stringRedisTemplate.opsForValue().increment("lock",1);
            int stock=Integer.parseInt(String.valueOf(stringRedisTemplate.opsForValue().get("stock")));
            while (stock>0&&num!=1){
                num=  stringRedisTemplate.opsForValue().increment("lock",1);
                stock=Integer.parseInt(String.valueOf(stringRedisTemplate.opsForValue().get("stock")));
            }
            // 防止获取锁后，减库存的业务突然挂掉，然后重启后锁得不到释放
            stringRedisTemplate.expire(lockKey,10,TimeUnit.SECONDS);
            if(num==1){
                if(stock>0){
                    stringRedisTemplate.opsForValue().set("stock",(stock-1)+"");
                    System.out.println(name+"---抢单成功，扣库存成功，库存stock"+(stock-1)+"");
                }else{
                    System.out.println("扣减失败，库存不足");
                }
            }
        }finally {
            stringRedisTemplate.opsForValue().increment("lock",-1);
        }
    }

    // 可以实现队列 自旋锁 如果锁被使用中自动增加时长
    @Autowired
    private Redisson redisson;
    @GetMapping("/test3")
    public void test3(String name){
        String lockKey="lockkey";
        RLock rlock=redisson.getLock(lockKey);
        try {
            rlock.lock();
            int stock=Integer.parseInt(String.valueOf(stringRedisTemplate.opsForValue().get("stock")));
            if(stock>0){
                stringRedisTemplate.opsForValue().set("stock",(stock-1)+"");
                System.out.println(name+"---抢单成功，扣库存成功，库存stock"+(stock-1)+"");
            }else{
                System.out.println("扣减失败，库存不足");
            }
        }finally {
            rlock.unlock();
        }
    }
    @Bean
    public Redisson redisson(){
        // 单机模式
        Config config=new Config();
        config.useSingleServer().setAddress("redis://127.0.0.1:6379").setDatabase(0);
        return (Redisson) Redisson.create(config);
    }

    /**
     * 模拟抢单动作
     */
    @GetMapping("/qiangdan/{method}")
    public void qiangdan(@PathVariable("method") String method) {

        //构造很多用户
        List<String> users = new ArrayList<>(100000);
        IntStream.range(0, 100000).parallel().forEach(b -> {
            users.add("测试--" + b);
        });
        //模拟开抢
        users.parallelStream().forEach(b -> {
            if(method.equals("test1")){
                test1(b);
            }else if(method.equals("test2")){
                test2(b);
            }else if(method.equals("test3")){
                test3(b);
            }
        });
    }
}
